import { Service, h, intersection } from "koishi"
import fsp from "fs/promises"
import { pathToFileURL } from "url"

export default class LNNBotSideloadFontsService extends Service {
  static name = "-lnnbot-sideload-fonts"

  constructor(ctx) {
    super(ctx, "lnnbotSideloadFonts", true)
  }

  async _scan(requireFamilies) {
    const fontDir = "assets/fonts"
    const families = Object.create(null)

    for (const fn of await fsp.readdir(fontDir)) {
      const match = fn.match(/^(?<family>.+?)(?:[ -]?(?<variant>Regular|Normal|(?<stretch>(?:Semi|Extra|Ultra)?(?:Condensed|Expanded))?[ -]?(?<weight>Thin|ExtraLight|Light|SemiLight|Medium|SemiBold|Bold|ExtraBold|Black|Heavy)?[ -]?(?<style>Italic|Oblique)?))?\.(?:ttf|otf|woff|woff2)$/i)
      if (!match) continue
      const src = pathToFileURL(fontDir + "/" + fn).href
      const { family, stretch, weight, style } = match.groups
      let f = families[family]
      if (!families[family]) {
        let names = [...new Set(family.split("~").flatMap(n => [n.trim(), n.replace(/\s+/g, "")]))]
        //if (requireFamilies) names = intersection(names, requireFamilies)
        f = {
          names,
          variants: [],
        }
      }
      if (requireFamilies && !intersection(f.names, requireFamilies).length) continue
      families[family] ??= f
      if (stretch) f.hasStretch = true
      if (weight) f.hasWeight = true
      if (style) f.hasStyle = true
      f.variants.push({
        src,
        stretch: stretch && stretch.toLowerCase().replace(/semi|extra|ultra/, "$&-"),
        weight: weight && { thin: 100, extralight: 200, light: 300, semilight: 350, medium: 500, semibold: 600, bold: 700, extrabold: 800, black: 900, heavy: 900 }[weight.toLowerCase()],
        style: style && style.toLowerCase(),
      })
    }

    for (const f of Object.values(families)) {
      if (!(f.hasStretch || f.hasWeight || f.hasStyle)) continue
      for (const v of f.variants) {
        if (f.hasStretch) v.stretch ??= "normal"
        if (f.hasWeight) v.weight ??= 400
        if (f.hasStyle) v.style ??= "normal"
      }
    }

    return families
  }

  async getDeclarations(requireFamilies) {
    return Object.values(await this._scan(requireFamilies))
      .flatMap(
        f => (requireFamilies ? intersection(f.names, requireFamilies) : f.names).flatMap(
          name => f.variants.flatMap(
            v => `@font-face{font-family:'${name}';${["stretch","weight","style"].filter(p => v[p]).map(p => `font-${p}:${v[p]};`).join("")}src:url('${v.src}')}`
          )
        )
      )
      .join("")
  }
}
